Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

two small changes to support native hardware features in embedded (for example hardware DMA and CRC in STM32) #55

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

kpvnnov
Copy link

@kpvnnov kpvnnov commented Jun 27, 2024

In order not to send data from one buffer to another, but to immediately receive data where it is needed (for example, in DMA mode), the buf_rec index is needed in structure nmbs_t.msg, it is used in the nmbs_platform_conf.read function
and nmbs_crc_calc requires the __weak attribute so that this function can be replaced with hardware calculation of the modbus CRC, a polynomial for which is also available in some embedded systems, including STM32
code examples for stm32 using HAL are attached

@kpvnnov
Copy link
Author

kpvnnov commented Jun 28, 2024

in HAL STM functions configurations are declared as __Weak to make override possible in case of other implementations in user file
and in headers files difined as

#if defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) /* ARM Compiler V6 /
#ifndef __weak
#define __weak attribute((weak))
#endif
#ifndef __packed
#define __packed attribute((packed))
#endif
#elif defined ( GNUC ) && !defined (__CC_ARM) /
GNU Compiler /
#ifndef __weak
#define __weak attribute((weak))
#endif /
__weak /
#ifndef __packed
#define __packed attribute((packed))
#endif /
__packed /
#endif /
GNUC */

@kpvnnov
Copy link
Author

kpvnnov commented Jun 28, 2024

or do we need any other way to use our crc16 function
to use the hardware capabilities of calculating the СRC as described in this application note an4187
https://www.st.com/resource/en/application_note/an4187-using-the-crc-peripheral-on-stm32-microcontrollers-stmicroelectronics.pdf

@debevv
Copy link
Owner

debevv commented Jul 24, 2024

Hi, thanks for the PR.

I don't know if the time spent on calculating the CRC in a typical modbus application justifies doing it via DMA, but I think giving the user the possibility to override the CRC calc function won't hurt. The only thing I would change is, instead of redefining the symbol, using the already established system of defining a function pointer inside the nmbs_platform_conf struct. I will take care of that.

Regarding the STM32 example, can you provide a way for the user to build it? I know on micros it's a bit messy to setup a shareable build environment, but even a simple makefile could be enough. Maybe one where the user must define the path to the STM32 SDK (which I guess should be the only thing needed to build this)

@kpvnnov
Copy link
Author

kpvnnov commented Aug 1, 2024

this helps to reduce the cost of the product. you can take a cheaper processor that can handle processing, for example, six streams (ports) at once at high speed (modbus gateway). I will make a ready-made example with a makefile a little later.

@debevv
Copy link
Owner

debevv commented Sep 20, 2024

Implemented in #64

@marcocipriani01
Copy link

Just for other people's reference, here's how I implemented hardware CRC calculation on my STM32H723ZG:

CRC_HandleTypeDef hcrc;
hcrc.Instance = CRC;
hcrc.Init.DefaultPolynomialUse = DEFAULT_POLYNOMIAL_DISABLE;
hcrc.Init.DefaultInitValueUse = DEFAULT_INIT_VALUE_DISABLE;
hcrc.Init.GeneratingPolynomial = 0x8005;
hcrc.Init.CRCLength = CRC_POLYLENGTH_16B;
hcrc.Init.InitValue = 0xFFFF;
hcrc.Init.InputDataInversionMode = CRC_INPUTDATA_INVERSION_BYTE;
hcrc.Init.OutputDataInversionMode = CRC_OUTPUTDATA_INVERSION_ENABLE;
hcrc.InputDataFormat = CRC_INPUTDATA_FORMAT_BYTES;
if (HAL_CRC_Init(&hcrc) != HAL_OK) {
    Error_Handler();
}
uint16_t modbusHardwareCRC(const uint8_t* data, uint32_t length, void* arg) {
    const uint16_t crc = HAL_CRC_Calculate(&hcrc, (uint32_t*) data, length);
    return ((uint16_t) (crc << 8)) | ((uint16_t) (crc >> 8));
}

In my testing, hardware CRC was ~10 times faster than software CRC. The only drawback of hardware CRC is that it needs to be protected by a mutex if multiple tasks need it, which slows things down significantly. Here's my workaround, which is still, on average, ~5 times faster than software CRC:

uint16_t modbusHardwareCRC(const uint8_t* data, uint32_t length, void* arg) {
    if (osMutexAcquire(crcMutex, 0UL) == osOK) {
        if (HAL_CRC_GetState(&hcrc) == HAL_CRC_STATE_READY) {
            const uint16_t crc = HAL_CRC_Calculate(&hcrc, (uint32_t*) data, length);
            osMutexRelease(crcMutex);
            return ((uint16_t) (crc << 8)) | ((uint16_t) (crc >> 8));
        }
        osMutexRelease(crcMutex);
        return nmbs_crc_calc(data, length, arg);
    }
    return nmbs_crc_calc(data, length, arg);
}

I got Modbus over TCP working with the lwIP network stack of the STM32. I'll be testing UART with DMA soon. I'll see what I can share. So far, the library has been rock solid during testing.

@debevv
Copy link
Owner

debevv commented Sep 23, 2024

@marcocipriani01 yes, an STM32 example with lwIP and a custom CRC function would be very useful

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants